* 2007/05/23: add optional user bitmap
* 2007/06/02: new method to compute center (mean) of bounds
avoid endless loop in group splitting
+ * 2007/07/10: put address fields (i.e. city) into GMSD
+ * 2007/07/12: add write support for new address fields
ToDo:
#include "defs.h"
#include "cet_util.h"
#include "jeeps/gpsmath.h"
+#include "garmin_fs.h"
#include "garmin_gpi.h"
#include <ctype.h>
#include <stdio.h>
#define DEFAULT_ICON "Waypoint"
#define WAYPOINTS_PER_BLOCK 128
+/* flags used in the gpi address mask */
+#define GPI_ADDR_CITY 1
+#define GPI_ADDR_COUNTRY 2
+#define GPI_ADDR_STATE 4
+#define GPI_ADDR_POSTAL_CODE 8
+#define GPI_ADDR_ADDR 16
+
static char *opt_cat, *opt_pos, *opt_notes, *opt_hide_bitmap, *opt_descr, *opt_bitmap;
static arglist_t garmin_gpi_args[] = {
struct writer_data_s *buttom_left;
struct writer_data_s *buttom_right;
} writer_data_t;
+
+typedef struct gpi_waypt_data_s {
+ int sz;
+ char *addr;
+ char *postal_code;
+} gpi_waypt_data_t;
typedef struct {
gbint32 size;
gbint32 size_2c;
} gpi_bitmap_header_t;
+typedef struct {
+ int sz;
+ short mask;
+ char addr_is_dynamic;
+ char *addr;
+ char *city;
+ char *country;
+ char *phone_nr;
+ char *postal_code;
+ char *state;
+} gpi_waypt_t;
static gbfile *fin, *fout;
static gbint32 codepage; /* code-page, i.e. 1252 */
static short_handle short_h;
#ifdef GPI_DBG
-# define PP printf("@%1$6x (%1$8d): ", gbftell(fin))
+# define PP warning("@%1$6x (%1$8d): ", gbftell(fin))
+# define dbginfo warning
#else
# define PP
#endif
* %%% gpi reader %%% *
*******************************************************************************/
-/* read a string with embedded "EN" header */
-static char *
-read_string(const int *sz)
+/* look for or initialize GMSD */
+static garmin_fs_t *
+gpi_gmsd_init(waypoint *wpt)
{
- char en[3];
- short slen;
- char *res;
-
- gbfread(&en, 1, 2, fin);
-
- en[2] = '\0';
- if (strcmp(en, "EN") != 0) {
- fatal(MYNAME ": Out of sync ('EN' expected)!\n");
+ if (wpt == NULL) {
+ fatal(MYNAME ": Error in file structure.\n");
}
-
- slen = gbfgetint16(fin);
- if (sz && (*sz != slen + 4)) {
- fatal(MYNAME ": Out of sync (wrong string size)!\n");
+ garmin_fs_t *gmsd = GMSD_FIND(wpt);
+ if (gmsd == NULL) {
+ gmsd = garmin_fs_alloc(-1);
+ fs_chain_add(&wpt->fs, (format_specific_data *) gmsd);
}
+ return gmsd;
+}
+
+/* read a standard string with or without 'EN' (or whatever) header */
+static char *
+gpi_read_string(const char *field)
+{
+ int l1;
+ char *res = NULL;
- if (slen > 0) {
- res = xmalloc(slen + 1);
- res[slen] = '\0';
- gbfread(res, 1, slen, fin);
+ l1 = gbfgetint16(fin);
+ if (l1 > 0) {
+ short l2;
+ char first;
+
+ first = gbfgetc(fin);
+ if (first == 0) {
+ char en[2];
+
+ is_fatal((gbfgetc(fin) != 0),
+ MYNAME ": Error reading field '%s'!", field);
+
+ gbfread(en, 1, sizeof(en), fin);
+ l2 = gbfgetint16(fin);
+ is_fatal((l2 + 4 != l1),
+ MYNAME ": Error out of sync (wrong size %d/%d) on field '%s'!", l1, l2, field);
+
+ if ((en[0] < 'A') || (en[0] > 'Z') || (en[1] < 'A') || (en[1] > 'Z'))
+ fatal(MYNAME ": Invalid country code!\n");
+ res = xmalloc(l2 + 1);
+ res[l2] = '\0';
+ PP;
+ if (l2 > 0)
+ gbfread(res, 1, l2, fin);
+ }
+ else {
+ res = xmalloc(l1 + 1);
+ *res = first;
+ *(res + l1) = '\0';
+ PP;
+ l1--;
+ if (l1 > 0)
+ gbfread(res + 1, 1, l1, fin);
+
+ }
}
- else res = NULL;
-
+#ifdef GPI_DBG
+ dbginfo("%s: %s\n", field, (res == NULL) ? "<NULL>" : res);
+#endif
return res;
}
-
static void
read_header(void)
{
tm.tm_year += 20; /* !!! */
tm.tm_mday -= 1; /* !!! */
strftime(stime, sizeof(stime), "%Y/%m/%d %H:%M:%S", &tm);
- printf("crdate = %lu (%s)\n", rdata->crdate, stime);
+ dbginfo("crdate = %lu (%s)\n", rdata->crdate, stime);
#endif
(void) gbfgetint16(fin); /* 0 */
#ifdef GPI_DBG
PP;
- printf("< leaving header\n");
+ dbginfo("< leaving header\n");
#endif
}
int pos, len;
waypoint *wpt;
-#if GPI_DBG
+#ifdef GPI_DBG
PP;
- printf("> reading poi (size %d)\n", sz);
+ dbginfo("> reading poi (size %d)\n", sz);
#endif
PP;
len = gbfgetint32(fin); /* sub-header size */
-#if GPI_DBG
- printf("poi sublen = %1$d (0x%1$x)\n", len);
+#ifdef GPI_DBG
+ dbginfo("poi sublen = %1$d (0x%1$x)\n", len);
#endif
pos = gbftell(fin);
(void) gbfgetint16(fin); /* ? always 1 ? */
(void) gbfgetc(fin); /* seems to 1 when extra options present */
- len = gbfgetint32(fin);
-
- PP;
- wpt->shortname = read_string(&len);
-#ifdef GPI_DBG
- printf("shortname = %s\n", wpt->shortname);
-#endif
+ wpt->shortname = gpi_read_string("Shortname");
while (gbftell(fin) < (gbsize_t)(pos + sz - 4)) {
int tag = gbfgetint32(fin);
#ifdef GPI_DBG
PP;
- printf("< leaving poi\n");
+ dbginfo("< leaving poi\n");
#endif
}
pos = gbftell(fin);
#ifdef GPI_DBG
PP;
- printf("> reading poi list (-> %1$x / %1$d )\n", pos + sz);
+ dbginfo("> reading poi list (-> %1$x / %1$d )\n", pos + sz);
#endif
PP;
i = gbfgetint32(fin); /* mostly 23 (0x17) */
#ifdef GPI_DBG
- printf("list sublen = %1$d (0x%1$x)\n", i);
+ dbginfo("list sublen = %1$d (0x%1$x)\n", i);
#endif
(void) gbfgetint32(fin); /* max-lat */
(void) gbfgetint32(fin); /* max-lon */
}
#ifdef GPI_DBG
PP;
- printf("< leaving poi list\n");
+ dbginfo("< leaving poi list\n");
#endif
}
static void
read_poi_group(const int sz, const int tag)
{
- int len, pos;
+ int pos;
pos = gbftell(fin);
#ifdef GPI_DBG
PP;
- printf("> reading poi group (-> %1$x / %1$d)\n", pos + sz);
+ dbginfo("> reading poi group (-> %1$x / %1$d)\n", pos + sz);
#endif
if (tag == 0x80009) {
int subsz;
PP;
subsz = gbfgetint32(fin); /* ? offset to category data ? */
#ifdef GPI_DBG
- printf("group sublen = %d (-> %x / %d)\n", subsz, pos + subsz + 4, pos + subsz + 4);
+ dbginfo("group sublen = %d (-> %x / %d)\n", subsz, pos + subsz + 4, pos + subsz + 4);
#endif
}
+ if (rdata->group) xfree(rdata->group); /* currently unused */
+ rdata->group = gpi_read_string("Group");
- len = gbfgetint32(fin); /* size of group string */
- PP;
- if (rdata->group) xfree(rdata->group);
- rdata->group = read_string(&len);
-#ifdef GPI_DBG
- printf("Group \"%s\"\n", rdata->group);
-#endif
while (gbftell(fin) < (gbsize_t)(pos + sz)) {
int subtag = gbfgetint32(fin);
if (! read_tag("read_poi_group", subtag, NULL)) break;
#ifdef GPI_DBG
PP;
- printf("< leaving poi group\n");
+ dbginfo("< leaving poi group\n");
#endif
}
static int
read_tag(const char *caller, const int tag, waypoint *wpt)
{
- int pos, sz, len, flag;
+ int pos, sz;
+ short mask;
char *str;
-#ifdef GPI_DBG
- int subtag;
-#endif
-
+ garmin_fs_t *gmsd;
+
sz = gbfgetint32(fin);
pos = gbftell(fin);
#ifdef GPI_DBG
PP;
- printf("%s: tag = 0x%x (size %d)\n", caller, tag, sz);
+ dbginfo("%s: tag = 0x%x (size %d)\n", caller, tag, sz);
#endif
if ((tag >= 0x80000) && (tag <= 0x800ff)) sz += 4;
case 0x5: /* group bitmap */
break;
- case 0x7: /* category */
- (void) gbfgetint32(fin);
- (void) gbfgetint16(fin);
+ case 0x7:
+ (void) gbfgetint16(fin); /* category number */
if (rdata->category) xfree(rdata->category);
- PP;
- rdata->category = read_string(NULL);
-#ifdef GPI_DBG
- printf("Category: \"%s\"\n", rdata->category);
-#endif
+ rdata->category = gpi_read_string("Category");
break;
- case 0xa: /* description */
- len = gbfgetint32(fin);
- PP;
- wpt->description = read_string(&len);
-#ifdef GPI_DBG
- printf("Description: \"%s\"\n", wpt->description);
-#endif
+ case 0xa:
+ wpt->description = gpi_read_string("Description");
break;
case 0xe: /* ? notes or description / or both ? */
- flag = gbfgetc(fin);
- if (flag == 0x01) {
- len = gbfgetint32(fin);
- PP;
- str = read_string(NULL);
+ mask = gbfgetc(fin);
+ if (mask == 0x01) {
+ str = gpi_read_string("Notes");
}
- else if (flag == 0x32) {
- len = gbfgetint16(fin);
- str = xmalloc(len + 1);
- str[len] = '\0';
- PP;
- gbfread(str, 1, len, fin);
+ else if (mask == 0x32) {
+ str = gpi_read_string("Notes");
}
else break;
-#ifdef GPI_DBG
- printf("Notes: \"%s\"\n", str);
-#endif
+
if (wpt->description) wpt->notes = str;
else wpt->description = str;
break;
break;
case 0x8000b: /* address (street/city...) */
- /* ToDo */
+ (void) gbfgetint32(fin);
+ PP;
+ mask = gbfgetint16(fin); /* address fields mask */
+#ifdef GPI_DBG
+ dbginfo("GPI Address field mask: %d (0x%02x)\n", mask, mask);
+#endif
+ if ((mask & GPI_ADDR_CITY) && (str = gpi_read_string("City"))) {
+ gmsd = gpi_gmsd_init(wpt);
+ GMSD_SET(city, str);
+ }
+ if ((mask & GPI_ADDR_COUNTRY) && (str = gpi_read_string("Country"))) {
+ gmsd = gpi_gmsd_init(wpt);
+ GMSD_SET(country, str);
+ }
+ if ((mask & GPI_ADDR_STATE) && (str = gpi_read_string("State"))) {
+ gmsd = gpi_gmsd_init(wpt);
+ GMSD_SET(state, str);
+ }
+ if ((mask & GPI_ADDR_POSTAL_CODE) && (str = gpi_read_string("Postal code"))) {
+ gmsd = gpi_gmsd_init(wpt);
+ GMSD_SET(postal_code, str);
+ }
+ if ((mask & GPI_ADDR_ADDR) && (str = gpi_read_string("Street address"))) {
+ gmsd = gpi_gmsd_init(wpt);
+ GMSD_SET(addr, str);
+ }
break;
case 0x8000c: /* phone-number */
-#ifdef GPI_DBG
- PP;
- subtag = gbfgetint32(fin);
- printf("phone-number tag %d\n", subtag);
- (void) gbfgetint16(fin); /* unknown / ? phone/fax/mobil ? */
- PP;
- len = gbfgetint16(fin);
- printf("phone-number len %d\n", len);
- str = xmalloc(len + 1);
- str[len] = '\0';
+ (void) gbfgetint32(fin);
PP;
- gbfread(str, 1, len, fin);
- printf("phone-number \"%s\"\n", str);
- xfree(str);
+
+ mask = gbfgetint16(fin); /* phone fields mask */
+#ifdef GPI_DBG
+ dbginfo("GPI Phone field mask: %d (0x%02x)\n", mask, mask);
#endif
+ if ((mask & 1) && (str = gpi_read_string("Phone"))) {
+ gmsd = gpi_gmsd_init(wpt);
+ GMSD_SET(phone_nr, str);
+ }
break;
case 0x80012: /* ? sounds / images ? */
*******************************************************************************/
static void
-write_string(const char *str)
+write_string(const char *str, const char long_format)
{
int len;
len = strlen(str);
-
- gbfputint32(len + 4, fout);
- gbfwrite("EN", 1, 2, fout);
+ if (long_format) {
+ gbfputint32(len + 4, fout);
+ gbfwrite("EN", 1, 2, fout);
+ }
gbfputint16(len, fout);
gbfwrite(str, 1, len, fout);
}
QUEUE_FOR_EACH(&data->Q, elem, tmp) {
waypoint *wpt = (waypoint *)elem;
- if (wpt->extra_data) xfree(wpt->extra_data);
+ if (wpt->extra_data) {
+ gpi_waypt_t *dt = (gpi_waypt_t *) wpt->extra_data;
+ if (dt->addr_is_dynamic) xfree(dt->addr);
+ xfree(dt);
+ }
waypt_free(wpt);
}
QUEUE_FOR_EACH(&data->Q, elem, tmp) {
waypoint *wpt = (waypoint *) elem;
+ gpi_waypt_t *dt;
+ garmin_fs_t *gmsd;
char *str;
res += 12; /* tag/sz/sub-sz */
else if (opt_pos)
str = pretty_deg_format(wpt->latitude, wpt->longitude, 's', " ", 0);
- if (str) { /* this will be stored into street-address field */
- res += 22 + strlen(str);
- wpt->extra_data = str;
+ dt = xcalloc(1, sizeof(*dt));
+ wpt->extra_data = dt;
+
+ if (str) {
+ dt->addr_is_dynamic = 1;
+ dt->addr = str;
+ dt->mask |= GPI_ADDR_ADDR;
+ dt->sz += (8 + strlen(dt->addr));
+ }
+
+ if ((gmsd = GMSD_FIND(wpt))) {
+ if ((dt->mask == 0) && ((dt->addr = GMSD_GET(addr, NULL)))) {
+ dt->mask |= GPI_ADDR_ADDR;
+ dt->sz += (8 + strlen(dt->addr));
+ }
+ if ((dt->city = GMSD_GET(city, NULL))) {
+ dt->mask |= GPI_ADDR_CITY;
+ dt->sz += (8 + strlen(dt->city));
+ }
+ if ((dt->country = GMSD_GET(country, NULL))) {
+ dt->mask |= GPI_ADDR_COUNTRY;
+ dt->sz += (8 + strlen(dt->country));
+ }
+ if ((dt->state = GMSD_GET(state, NULL))) {
+ dt->mask |= GPI_ADDR_STATE;
+ dt->sz += (8 + strlen(dt->state));
+ }
+ if ((dt->postal_code = GMSD_GET(postal_code, NULL))) {
+ dt->mask |= GPI_ADDR_POSTAL_CODE;
+ dt->sz += (2 + strlen(dt->postal_code)); /* short form */
+ }
+
+ if ((dt->phone_nr = GMSD_GET(phone_nr, NULL)))
+ res += (12 + 4 + strlen(dt->phone_nr));
}
+ if (dt->mask) dt->sz += 2; /* + mask (two bytes) */
+ if (dt->sz) res += (dt->sz + 12); /* + header size */
str = wpt->description;
if (! str) str = wpt->notes;
- if (str) res += (16 + strlen(str));
+ if (str) res += (12 + 4 + strlen(str));
}
if (data->top_left) res += wdata_compute_size(data->top_left);
data->sz = res;
- return res + 12; /* 12 = caller needs info about tag header size */
+ return res + 12; /* + 12 = caller needs info about tag header size */
}
char *str;
int s0, s1;
waypoint *wpt = (waypoint *)elem;
+ gpi_waypt_t *dt = wpt->extra_data;
str = wpt->description;
if (! str) str = wpt->notes;
s0 = s1 = 19 + strlen(wpt->shortname);
s0 += 10; /* tag(4) */
- if (str) s0 += (16 + strlen(str)); /* descr */
-
- if (wpt->extra_data)
- s0 += 22 + strlen((char *)wpt->extra_data);
+ if (str) s0 += (12 + 4 + strlen(str)); /* descr */
+ if (dt->sz) s0 += (12 + dt->sz); /* address part */
+ if (dt->phone_nr) s0 += (12 + 4 + strlen(dt->phone_nr));
gbfputint32(s0, fout); /* size of following data (tag) */
gbfputint32(s1, fout); /* basic size (without options) */
gbfputint16(1, fout); /* ? always 1 ? */
gbfputc(0, fout); /* seems to be 1 when extra options present */
- write_string(wpt->shortname);
+ write_string(wpt->shortname, 1);
gbfputint32(4, fout); /* tag(4) */
- gbfputint32(2, fout);
+ gbfputint32(2, fout); /* ? always 2 == version ??? */
if (opt_hide_bitmap) gbfputint16(0x3ff, fout); /* values != 0 hides the bitmap */
else gbfputint16(0, fout);
if (str) {
gbfputint32(0xa, fout);
gbfputint32(strlen(str) + 8, fout); /* string + string header */
- write_string(str);
+ write_string(str, 1);
}
- str = (char *)wpt->extra_data;
- if (str) {
+ if (dt->sz) { /* gpi address */
gbfputint32(0x8000b, fout);
- gbfputint32(strlen(str) + 10, fout);
- gbfputint32(0x2, fout); /* ? always 2 ? */
- gbfputint16(0x10, fout); /* 0x10 = StreetAddress */
- write_string(str);
+ gbfputint32(dt->sz, fout);
+ gbfputint32(0x2, fout); /* ? always 2 ? */
+ gbfputint16(dt->mask, fout);
+ if (dt->mask & GPI_ADDR_CITY) write_string(dt->city, 1);
+ if (dt->mask & GPI_ADDR_COUNTRY) write_string(dt->country, 1);
+ if (dt->mask & GPI_ADDR_STATE) write_string(dt->state, 1);
+ if (dt->mask & GPI_ADDR_POSTAL_CODE) write_string(dt->postal_code, 0);
+ if (dt->mask & GPI_ADDR_ADDR) write_string(dt->addr, 1);
+ }
+
+ if (dt->phone_nr) {
+ gbfputint32(0x8000c, fout);
+ gbfputint32(strlen(dt->phone_nr) + 2 + 2, fout);
+ gbfputint32(0x2, fout); /* ? always 2 ? */
+ gbfputint16(1, fout); /* mask */
+ write_string(dt->phone_nr, 0);
}
}
else
gbfputint32(sz, fout);
gbfputint32(sz, fout);
- write_string(opt_cat);
+ write_string(opt_cat, 1);
wdata_write(wdata);
dest_line_sz = ((int)((dest_line_sz + 3) / 4)) * 4;
sz = sizeof(*dest_h) + (src_h.height * dest_line_sz);
- if (src_h.used_colors) sz += src_h.used_colors * 4;
+ if (src_h.used_colors) sz += (src_h.used_colors * 4);
ptr = xmalloc(sz);
dest_h = (void *)ptr;
/* copy and revert order of BMP lines */
ptr = (void *)dest_h;
- ptr += sizeof(*dest_h) + (dest_line_sz * (src_h.height - 1));
+ ptr += (sizeof(*dest_h) + (dest_line_sz * (src_h.height - 1)));
if (src_h.bpp == 24) {
/* 24 bpp seems to be not supported, convert to 32 bpp */
if (src_h.used_colors > 0) {
ptr = (void *)dest_h;
- ptr += sizeof(*dest_h) + (src_h.height * src_line_sz);
+ ptr += (sizeof(*dest_h) + (src_h.height * src_line_sz));
for (i = 0; i < src_h.used_colors; i++) {
le_write32(ptr, color_table[i]);
else if (opt_bitmap && *opt_bitmap)
load_bitmap_from_file(opt_bitmap, &image, &image_sz);
else {
- image = gpi_bitmap; /* embedded image in gpi format */
+ image = gpi_bitmap; /* embedded GPSBabel icon in gpi format */
image_sz = GPI_BITMAP_SIZE;
}
waypt_disp_all(enum_waypt_cb);